package gcs

import (
	"context"
	"fmt"
	"os/exec"
	"strings"
	"time"

	"gitee.com/liumou_site/logger"

	"gitee.com/liumou_site/gbm"
	"github.com/spf13/cast"
)

// RunShell 执行Shell命令。
//
// 该方法接受一个或多个字符串参数作为命令，将其存储在api.args中，
// 然后调用api.shellSystem()方法来执行命令。
// 此方法不返回任何值。
func (api *ApiShell) RunShell(command ...string) {
	// 将传入的命令参数存储到api实例的args字段中。
	api.args = command
	// 调用shellSystem方法执行存储在args中的命令。
	api.shellSystem()
}

// RunShellList 批量执行命令
//
//	@Description: 通过传入完整的命令切片遍历执行,当遇到执行错误立即返回
//	@receiver api
//	@param cs 需要执行的命令切片,每个切片元素必须是完整命令
func (api *ApiShell) RunShellList(cs []string) {
	for i, c := range cs {
		api.RunShell(c)
		if api.Err != nil {
			logs.Error("第[ %d ]条命令执行失败", i)
			fmt.Println(c)
			return
		}
	}
}

// RunScript 通过生成脚本的方式执行Shell，支持Shell管道
//
//	@Description:
//	@receiver api
//	@param command 需要执行的命令,例如 'apt', 'update',命令与选项可以分开传入
func (api *ApiShell) RunScript(command ...string) {
	api.ScriptMode = true
	api.args = command
	api.Text = gbm.SliceToString(command, " ")
	err := createScript(api.Text, api.Script, false)
	if err == nil {
		api.args = []string{api.Script}
		api.shellSystem()
	} else {
		if api.PrintErr {
			logs.Error("文件创建失败: ", api.Script)
		}
	}
	api.ScriptMode = false
}

// RunTerminal 通过调用图形化终端程序的方式执行Shell命令，使用脚本封装的方式,支持Shell管道
//
//	@Description:
//	@receiver api
//	@param command 需要执行的命令,例如 'apt', 'update',命令与选项可以分开传入
func (api *ApiShell) RunTerminal(command ...string) {
	if !api.TerminalBool {
		api.Err = fmt.Errorf("当前系统暂不支持终端执行命令")
		return
	}
	api.ScriptMode = true
	api.Text = api.Terminal + " " + api.TerminalArg + " " + gbm.SliceToString(command, " ")
	if api.Debug {
		logs.Debug(api.Text)
	}
	api.args = command
	if api.Debug {
		fmt.Println(api.args)
	}
	err := createScript(api.Text, api.Script, false)
	if err == nil {
		api.args = []string{api.Script}
		api.shellSystem()
	} else {
		if api.PrintErr {
			logs.Error("文件创建失败: ", api.Script)
		}
	}
	api.ScriptMode = false
}

// shellInit
//
//	@Description: 初始化执行参数
//	@receiver api
func (api *ApiShell) shellInit() {
	var argTmp string
	api.Strings = ""   // 初始化输出
	api.Result = false // 初始化执行结果
	api.Err = nil      // 初始化错误
	if api.Debug {
		logs.Debug("处理前参数: %d", len(api.args))
		fmt.Println(api.args)
	}
	api.args = gbm.SliceToSlice(api.args, " ") // 去除空元素
	if api.Debug {
		logs.Debug("处理后参数: %d", len(api.args))
		fmt.Println(api.args)
	}
	api.args = gbm.SliceRemoveNull(api.args)
	if len(api.args) == 1 { // 判断命令参数数量
		api.args = strings.Split(api.args[0], " ")
	}
	api.Text = gbm.SliceToString(api.args, " ")
	api.cmdName = "/bin/bash"  // 定义命令路径
	if CheckCmd(api.args[0]) { // 如果命令在PATH中则使用命令执行并从参数中移除命令
		api.cmdName = api.args[0]                             // 使用第一个参数作为命令
		api.args, api.Err = gbm.SliceRemoveIndex(api.args, 0) // 移除第一个元素
		if api.Err != nil {
			return
		}
		argTmp = ""
		api.argRun = api.args // 在切片头部添加/C参数
	} else {
		if api.OsType == "windows" {
			api.cmdName = "cmd"
			argTmp = "/C"
		} else {
			argTmp = "-c"
		}
		api.argRun = append([]string{argTmp}, api.args...) // 在切片头部添加-c参数
	}

}

// shellSystem 执行命令并将结果赋值到结构体
//
//	@Description:
//	@receiver api
func (api *ApiShell) shellSystem() {
	var cmd *exec.Cmd
	api.shellInit()
	if len(api.args) > 0 {
		cmd = exec.Command(api.cmdName, api.argRun...) // 创建命令实例
	} else {
		cmd = exec.Command(api.cmdName) // 创建命令实例
	}
	if api.Err != nil {
		return
	}
	if api.ScriptMode {
		if api.Debug {
			logs.Debug(api.Script)
		}
		if api.TerminalBool {
			cmd = exec.Command(api.Terminal, api.TerminalArg, api.Script)
		} else {
			cmd = exec.Command("/bin/bash", "-c", api.Script)
		}

	}

	// 命令的错误输出和标准输出都连接到同一个管道
	stdout, err := cmd.StdoutPipe()
	cmd.Stderr = cmd.Stdout
	if api.Ignore {
		// 如果开启了屏蔽标准输出，则设置nil
		cmd.Stdout = nil
	}
	if api.BlackHole {
		// 如果开启了黑洞屏蔽错误输出，则设置nil
		cmd.Stderr = nil
	}
	api.Err = err
	if err != nil {
		if api.PrintErr {
			logs.Error("管道创建失败")
		}
		if api.Debug {
			logs.Debug("Execute command: ", api.Text)
		}
		api.ExitCode = 1
		return
	}
	api.Err = cmd.Start()
	if api.Err != nil {
		logs.Error("命令启动失败 :", api.Err)
		logs.Debug("Execute command: ", api.Text)
		api.ExitCode = 2
		return
	}
	// 从管道中实时获取输出并打印到终端
	for {
		tmp := make([]byte, 1024)
		_, err := stdout.Read(tmp)
		// 如果开启了实时打印,则将信息逐行输出到终端
		if api.Realtime {
			fmt.Print(string(tmp))
		}
		api.Strings = api.Strings + cast.ToString(string(tmp))
		if err != nil {
			break
		}
	}

	// 等待命令退出，并等待任何复制到stdin或从stdout或stderr复制完成。
	api.Err = cmd.Wait()
	// ExitCode返回已退出进程的退出代码，如果进程尚未退出或被信号终止，则返回-1。
	api.ExitCode = cmd.ProcessState.ExitCode()
	if api.PrintInfo {
		logs.Info("Exit Code: ", cmd.ProcessState.ExitCode())
	}

	if api.Err == nil {
		if api.ExitCode != 0 {
			if api.PrintErr {
				logs.Error("Error :", api.Err)
			}
			if api.Debug {
				logs.Debug("Execute command: ", api.Text)
			}
		}
		if api.ExitCode == 0 {
			api.Result = true
			api.Err = nil
		}
	}
}

// argsHandle 参数处理
//
//	@Description:
//	@receiver api
func (api *ApiShell) argsHandle() {
	api.Strings = ""   // 初始化输出
	api.Result = false // 初始化执行结果
	api.Err = nil      // 初始化错误
	api.Result = false // 重置执行结果
	if api.Debug {
		logs.Debug("处理前参数: %d", len(api.args))
	}
	api.args = gbm.SliceToSlice(api.args, " ") // 去除空元素
	if api.Debug {
		logs.Debug("处理后参数: %d", len(api.args))
	}
	api.args = append([]string{"-c"}, api.args...) // 在切片头部添加-S参数
}

// RunTimeout 执行系统命令并设置超时时间
//
//	@Description:
//	@receiver api
//	@param n 超时时间(秒)
//	@param command 需要执行的命令,例如 'apt', 'update',命令与选项可以分开传入
//	@return *ApiShell
func (api *ApiShell) RunTimeout(n time.Duration, command ...string) *ApiShell {
	api.args = command
	api.shellInit() // 处理参数
	if api.Err != nil {
		return api
	}
	ctx, cancel := context.WithTimeout(context.Background(), n*time.Second) // 创建带有超时时间的上下文对象
	defer cancel()
	if api.Debug {
		logs.Debug("执行命令: %s", api.cmdName)
		logs.Debug("执行参数: %s", api.argRun)
	}
	cmd := exec.CommandContext(ctx, api.cmdName, api.argRun...) // 创建命令对象，并将上下文对象传递给 CommandContext 方法
	// 执行命令，并等待命令执行完成
	if api.Err = cmd.Run(); api.Err != nil {
		// 如果是超时错误，则返回超时信息
		if ctx.Err() == context.DeadlineExceeded {
			if api.PrintErr {
				logger.Error("命令执行超时: ", api.Err.Error())
			}
		}
	}
	return api
}
